package net.qiqb.spring;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import net.qiqb.core.domain.annotation.AggregatePersistence;
import net.qiqb.core.domain.annotation.ExtendedAggregatePersistence;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.ResolvableType;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Method;
import java.util.*;

public class AggregatePersistenceManager implements BeanFactoryPostProcessor, SmartInitializingSingleton {

    /**
     * key：聚合名称
     */
    private final Map<String, AggregatePersistence<?>> aggregatePersistenceCache = new HashMap<>();

    private final Map<String, ExtendedAggregatePersistence<?>> extendedAggregatePersistenceHashMap = new HashMap<>();


    public AggregatePersistenceManager() {

    }

    public void registerBusinessHandler(String key, AggregatePersistence<?> aggregatePersistence) {
        this.aggregatePersistenceCache.put(key, aggregatePersistence);
    }

    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    private Class<?> getResolvableType(ResolvableType resolvableType, Class<?> type, int index) {
        if (resolvableType == null || ResolvableType.NONE == resolvableType) {
            return null;
        }
        if (type.equals(resolvableType.getRawClass())) {
            final ResolvableType[] generics = resolvableType.getGenerics();
            if (generics.length > index) {
                return generics[index].resolve();
            }
        }
        // 接口类型
        for (ResolvableType anInterface : resolvableType.getInterfaces()) {
            // 接口上定义接口
            for (ResolvableType superResType : anInterface.getInterfaces()) {
                Class<?> result = getResolvableType(superResType, type, index);
                if (result != null) {
                    return result;
                }
            }
            //
            Class<?> result = getResolvableType(anInterface, type, index);
            if (result != null) {
                return result;
            }
        }
        // 父类
        return getResolvableType(resolvableType.getSuperType(), type, index);
    }

    public AggregatePersistence<?> getAggregatePersistence(String name) {
        return this.aggregatePersistenceCache.get(name);
    }

    public ExtendedAggregatePersistence<?> getExtendedAggregatePersistence(String name) {
        return this.extendedAggregatePersistenceHashMap.get(name);
    }

    @Override
    public void afterSingletonsInstantiated() {
        final Map<String, AggregatePersistence> aggregatePersistenceMap = this.beanFactory.getBeansOfType(AggregatePersistence.class);
        final Map<String, ExtendedAggregatePersistence> extendedAggregatePersistenceMap = this.beanFactory.getBeansOfType(ExtendedAggregatePersistence.class);
        for (Map.Entry<String, AggregatePersistence> entry : aggregatePersistenceMap.entrySet()) {
            final String beanName = entry.getKey();
            final BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            String implTypeClassName = beanDefinition.getBeanClassName();
            if (!ObjectUtils.isEmpty(implTypeClassName)) {
                final Class<Object> declaringClass = ClassUtil.loadClass(implTypeClassName);
                final ResolvableType resolvableType = ResolvableType.forClass(declaringClass);
                final Class<?> aggregateRootClass = getResolvableType(resolvableType, AggregatePersistence.class, 0);

                if (aggregateRootClass != null) {
                    final String aggregateRootClassName = aggregateRootClass.getName();
                    final Optional<ExtendedAggregatePersistence> first = extendedAggregatePersistenceMap.values().stream().filter(e -> e.matchedAggregateRootName().equals(aggregateRootClassName)).findFirst();
                    if (first.isPresent()){
                        extendedAggregatePersistenceHashMap.put(aggregateRootClassName,first.get());
                    }
                    registerBusinessHandler(aggregateRootClassName, (AggregatePersistence) beanFactory.getBean(beanName));
                }
                continue;
            }
            if (beanDefinition instanceof AnnotatedBeanDefinition) {
                final MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata();
                if (factoryMethodMetadata != null) {
                    // 找AggregateRootLoader 的敢兴趣的类
                    final String returnTypeName = factoryMethodMetadata.getReturnTypeName();
                    final Class<Object> declaringClass = ClassUtil.loadClass(factoryMethodMetadata.getDeclaringClassName());
                    // 因为现在获取不到参数，所以只能通过变量来获取
                    final Method[] methods = ReflectUtil.getPublicMethods(declaringClass);
                    final Optional<Method> matchedMethod = Arrays.stream(methods).filter(m -> m.getName().equals(factoryMethodMetadata.getMethodName())).findAny();
                    if (matchedMethod.isPresent()) {
                        final ResolvableType resolvableType = ResolvableType.forMethodReturnType(matchedMethod.get());
                        final ResolvableType[] generics = resolvableType.getGenerics();
                        if (generics.length > 0) {
                            String aggregateRootName = Objects.requireNonNull(generics[0].getRawClass()).getName();
                            final Optional<ExtendedAggregatePersistence> first = extendedAggregatePersistenceMap.values().stream().filter(e -> e.matchedAggregateRootName().equals(aggregateRootName)).findFirst();
                            if (first.isPresent()){
                                extendedAggregatePersistenceHashMap.put(aggregateRootName,first.get());
                            }
                            registerBusinessHandler(aggregateRootName, (AggregatePersistence) beanFactory.getBean(beanName));
                        }
                    }
                }
            }



        }
    }
}
